import logging
import uuid
import webapp2

from google.appengine.api import channel
from google.appengine.api import taskqueue
from google.appengine.api import users

from google.appengine.ext import db

from lib import auth
from lib import cache
from lib import models
from lib import utils
import config


def GetAuthResponse(account):
  if account.user:
    return {
      'status': 'logged-in',
    }
  else:
    return {
      'status': 'anonymous',
    }


class CheckAuth(webapp2.RequestHandler):

  def _SetResponseAccount(self, account):
    self.response.headers['Auth-Token'] = auth.GenerateToken(account)

  def _FindOrCreateAccount(self, google_user):
    accounts = models.Account.all().filter('user =', google_user).fetch(1)
    if accounts:
      account = accounts[0]
    else:
      account = models.Account(user=google_user)
      account.put()
    return account

  @utils.weak_security_checks
  @utils.returns_json
  @utils.chaos_monkey
  def post(self):
    google_user = users.get_current_user()
    user_token = self.request.headers.get('Auth-Token', None)

    if user_token:
      contents = auth.ParseToken(user_token)
      account = models.Account.get(contents['account'])

      # If the user has logged out of Google but still holds a valid token from
      # us, we let them continue to use that account. We don't have a framework
      # here to invalidate the token.

      if account.user == google_user:
        # Users match, all good
        return GetAuthResponse(account)
      else:  # Account and Google user mismatch
        if account.user:
          account = self._FindOrCreateAccount(google_user)
          self._SetResponseAccount(account)
          return GetAuthResponse(account)
        else:  # Account was anonymous
          # We can't do the merge here, or we'd have an XSRF hole where
          # an attacker could create an anonymous account and then merge
          # it in to a victim's account.
          ret = GetAuthResponse(account)
          ret.update({
            'status': 'merge',
            'merge_into': auth.GenerateToken(self._FindOrCreateAccount(google_user)),
          })
          return ret

    else:  # User didn't supply a token
      if google_user:
        account = self._FindOrCreateAccount(google_user)
        self._SetResponseAccount(account)
        return GetAuthResponse(account)
      else:  # User not logged in
        # Create an anonymous account
        account = models.Account()
        account.put()
        self._SetResponseAccount(account)
        return GetAuthResponse(account)


class MergeAccounts(webapp2.RequestHandler):
  @utils.weak_security_checks
  @auth.account_required
  @utils.returns_json
  @utils.chaos_monkey
  def post(self):
    merge_into_token = self.request.get('merge_into')
    contents = auth.ParseToken(merge_into_token)
    merge_into = models.Account.get(contents['account'])

    # Copy values from self.account to merge_into

    self.response.headers['Auth-Token'] = str(merge_into_token)
    return GetAuthResponse(merge_into)


@db.transactional
def _Subscribe(channel, feed_key):
  feed = models.Feed.get(feed_key)
  models.Subscription(parent=feed,
                      feed=feed,
                      channel=channel).put()
  if not feed.active:
    feed.active = True
    feed.put()
    taskqueue.add(queue_name='feeds',
                  url='/services/pollFeed',
                  params={'key': feed.key()},
                  transactional=True)
  

def Subscribe(channel, feed_path):
  feeds = models.Feed.all().filter('path =', feed_path).fetch(1)
  if feeds:
    feed = feeds[0]
  else:
    feed = models.Feed(path=feed_path)
    feed.put()

  _Subscribe(channel, feed.key())

  ret = []
  for media in feed.media_set:
    ret.append({
      'type': 'media-add',
      'feed': feed.path,
      'value': media.to_dict(),
    })
  return ret


class CreateChannel(webapp2.RequestHandler):
  @utils.weak_security_checks
  @cache.uses_cache
  @auth.account_required
  @utils.returns_json
  @utils.chaos_monkey
  def post(self):
    client_id = str(uuid.uuid1())
    token = channel.create_channel(client_id=client_id,
                                   duration_minutes=config.CHANNEL_DURATION_SECONDS / 60)
    channel_obj = models.Channel(parent=self.account,
                                 account=self.account,
                                 client_id=client_id,
                                 token=token)
    channel_obj.put()
    messages = Subscribe(channel_obj, '/youtube/channel/flamingcowtv')
    return {
      'channel-token': token,
      'messages': messages,
    }


app = webapp2.WSGIApplication([
  ('/api/checkAuth', CheckAuth),
  ('/api/createChannel', CreateChannel),
  ('/api/mergeAccounts', MergeAccounts),
])
